from qa_engine.handler.base import BaseHandler
from qa_engine.aiml import task_response
from qa_engine.aiml.entity import EntityExtractor, Entity, EntityType
from qa_engine.user import User
from neo4j import get_neo4j_connect
from server.config import neo4j_url, neo4j_user_name, neo4j_password


class EntityQAHandler(BaseHandler):
    """
    实体问答处理程序
    """

    def handle(self, question: str, user: User = None, _position: str = ''):
        """
        根据输入回应, handler接口
        :param question: 问题
        :param user: 用户
        :param _position: 位置
        :return: 如果能够回应, 则返回回应 格式 str[], 否则 None
        """
        # 对每个注册的实体提取器
        for each_extractor in self.entity_extractors:
            # 按提取器的配置提取实体, 得到提取实体之后被替换成AIML的代码,
            # 和对应的实体字典
            extracted_question, entity_dict = each_extractor.extract(question)

            # TODO: 目前未知为何要这样做: 反转字符串
            extracted_question = extracted_question[::-1]

            # 得到aiml响应
            respond = ''
            for aiml_kernel in self.aiml_kernels.values():
                respond = aiml_kernel.respond(extracted_question)
                if respond and len(respond) > 0:
                    break
            if 'task_' in respond:
                respond = task_response(
                    respond, entity_dict, question, user
                )
                return respond
            elif respond and len(respond) > 0:
                return [respond]
        # 都不能处理就返回None
        return None

    def __init__(self, config: dict):
        """
        :param config: AIML的配置
        """
        super().__init__(config)
        self._register_stop_word()
        self._register_entity_type()
        self._register_entity_extractor()

    def _register_entity_extractor(self):
        """
        读取实体提取器的配置
        :return:
        """
        self.entity_extractors = []
        for each_extractor in self.config['entity-extractor']:
            focus_on = set()
            for each in each_extractor['focus-on']:
                try:
                    focus_on.add(self.entity_types[each])
                except KeyError:
                    raise Exception('entity type not found: %s' % each)
            ignore_entity = None
            if 'ignore' in each_extractor.keys() and each_extractor['ignore']:
                ignore_entity = set()
                for each_ignore_entity in each_extractor['ignore']:
                    try:
                        for each in self.name2entities[each_ignore_entity]:
                            ignore_entity.add(each)
                    except KeyError:
                        from sys import stderr
                        stderr.write(
                            'entity extractor: ignore entity type '
                            'not found: %s\n' % each_ignore_entity
                        )
            self.entity_extractors.append(
                EntityExtractor(
                    focus_on=focus_on,
                    name2entities=self.name2entities,
                    ignore_entity=ignore_entity,
                    stop_word=self.stop_word
                )
            )

    def _register_stop_word(self):
        """
        读取AIML实体停用词配置
        :return:
        """
        self.stop_word = set()
        for each in self.config['entity-stop-word']:
            if each and len(each) > 0:
                self.stop_word.add(each)

    def _register_entity_type(self):
        """
        读取实体类型的配置, 并从neo4j数据库读取所有实体数据
        :return:
        """
        self.entity_types = {}
        # TODO: 这应该做成数据库查询的操作, 比如先查询label为name的是否存在,
        #  并根据name到实体的关系比如说belongs_to查到实体本身再返回
        # 名字到实体列表(因为可能是多对多映射)的映射, 相当于小型数据库
        self.name2entities = {}
        for each_type in self.config['entity-type']:
            self.entity_types[each_type['type-name']] = \
                EntityType(
                    each_type['type-name'],
                    each_type['aiml-code'],
                    each_type['node-label']
                )
        graph = get_neo4j_connect(neo4j_url, neo4j_user_name, neo4j_password)
        for each_type in self.entity_types.values():
            cursor = graph.run(
                'match(n:`%s`)return n.office_name as proper_name ,'
                'n.variant_name as alias' %
                each_type.node_label
            )
            while cursor.forward():
                record = dict(cursor.current())
                proper_name = record['proper_name']
                if not proper_name or len(proper_name) == 0:
                    continue
                if record['alias'] and record['alias'].find(u'，') != -1:
                    aliases = record['alias'].split(u'，')
                else:
                    aliases = [record['alias']]
                aliases = sorted(aliases, key=lambda i: len(i) if i else 0,
                                 reverse=True)
                entity = Entity(proper_name, each_type)
                if proper_name in self.name2entities.keys():
                    self.name2entities[proper_name].add(entity)
                else:
                    self.name2entities[proper_name] = {entity}
                for each_alias in aliases:
                    if each_alias and each_alias in self.name2entities.keys():
                        self.name2entities[each_alias].add(entity)
                    else:
                        self.name2entities[each_alias] = {entity}
        # 由集合转变成列表
        for each_name in self.name2entities.keys():
            self.name2entities[each_name] = \
                list(self.name2entities[each_name])
